# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__docformat__ = 'restructuredtext en'


from .. import hint
from .. import pilelib as stack
from ..forms import gypsy as GypsyLayout
from ..gamelib import info as gi, game
from ..packlib import info as cs, place
from ..piles import talon as TalonStack
from ..utillib.misc import kwdefault

Wrap = stack.StackWrapper
GT, SL = gi.GT, gi.SL


class GapTopACRowstack(stack.GapTopBuildMethod, stack.AC_RowStack):
    """
    Build in rank by alt color. Base any top card.
    """
    pass


class CastlesInSpain(game.Game):
    """
    Finish 1 deck on foundation with only row top open.  Row build alt color
    and play super move.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [13 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SuperMoveAC_RowStack
        Init 4 card per. Build down in rank by alt color. Play top or sequence
        if possible as seperate plays.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9c93, name='Castles in Spain',
                    game_type=GT.BAKERS_DOZEN, decks=1,
                    skill_level=SL.BALANCED, version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _layout_ = GypsyLayout.GypsyLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.SS_Foundation
    _row_ = stack.SuperMoveAC_RowStack
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=13, ncols=7, playcards=9]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.freecell` for keyword definitions.
        """
        kwdefault(kw, rows=13, ncols=7, playcards=9)
        return super(CastlesInSpain, self)._create_game(**kw)

    def _start(self, flip=(False,) * 3, **kw):
        """
        _start([flip=(False,) * 3]) -> None

        Deal 4 card per row.
        """
        for f in flip:
            self.s['talon'].dealRow(flip=f, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()


class Martha(CastlesInSpain):
    """
    Finish 1 deck on foundation with row alt open.

    :CVariables:
      _layout_ : layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [12 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Init ace per. Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.bakersdozen.GapTopACRowstack
        Init 4 alt open card per. Build down in rank by alt color. Play seq.
        Base top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9c94, name='Martha', game_type=GT.BAKERS_DOZEN,
                    decks=1, skill_level=SL.BALANCED, version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _row_ = GapTopACRowstack

    def _create_game(self, **kw):
        """
        _create_game([rows=12, ncols=6, playcards=13]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.gypsy` for keyword definitions.
        """
        kwdefault(kw, rows=12, ncols=6, playcards=13)
        return super(Martha, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by rank, suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, **kw):
        """
        _start([flip=(False, True, False)]) -> None

        Deal ace per foundation. Deal 4 card per row alt open.
        """
        self.s['talon'].dealRow(rows=self.s['foundations'])
        kw.setdefault('flip', (False, True, False))
        super(Martha, self)._start(**kw)


class PortugueseSolitaire(CastlesInSpain):
    """
    Finish 1 deck on foundation with only row top open. Row build rank alone
    and play top.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout

        - row [13 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon

        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_Rowstack (max_move=1, base_rank=KING)
        Init 4 card per. Build down in rank alone. Play top. Base king.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9d74, name='Portuguese Solitaire',
                    game_type=(GT.BAKERS_DOZEN | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.RK_RowStack, max_move=1, base_rank=cs.KING)


class BakersDozen(CastlesInSpain):
    """
    Finish 1 deck on foundation with row open. Row build rank alone
    and play top. Base none

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout

        - row [13 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon

        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_Rowstack (max_move=1, max_accept=1,
          base_rank=GAP)
        Init 4 open card per with base king. Build down in rank alone. Play
        top. Base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9c5f, name='Bakerʼs Dozen',
                    game_type=(GT.BAKERS_DOZEN | GT.OPEN | GT.POPULAR |
                    GT.GNOME | GT.XPAT), decks=1, version='2.90',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _row_ = Wrap(stack.RK_RowStack, max_move=1, max_accept=1,
                 base_rank=cs.GAP)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return KING as base of pile for _row_ deal.
        """
        return place.placeBaseCard(cards, len(self.s['rows']), cs.KING)

    def _start(self, **kw):
        """
        _start([flip=(True) * 3]) -> None

        Deal 4 card per row open with base king.
        """
        kw.setdefault('flip', (True,) * 3)
        return super(BakersDozen, self)._start(**kw)


class SpanishPatience(BakersDozen):
    """
    Finish 1 deck on foundation with row open. Row build rank alone
    and play top. Base none

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout

        - row [13 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon

        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.AC_Rowstack
        Init 4 open card per with base king. Build down in rank by alt color.
        Play sequence.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9c95, name='Spanish Patience',
                    game_type=(GT.BAKERS_DOZEN | GT.OPEN), decks=1,
                    version='3.00', skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundation_ = stack.AC_Foundation


class SpanishPatienceII(PortugueseSolitaire):
    """
    Finish 1 deck on foundation with only row top open. Row build rank alone
    and play top.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout

        - row [13 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon

        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_Rowstack (max_move=1)
        Init 4 card per. Build down in rank alone. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9ed8, name='Spanish Patience Ⅱ',
                    game_type=(GT.BAKERS_DOZEN | GT.OPEN), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.94fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.RK_RowStack, max_move=1)


class GoodMeasure(BakersDozen):
    """
    Finish 1 deck on foundation with row open. Row build rank alone
    and play top. Base none

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout

        - row [10 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon

        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.RK_Rowstack (max_move=1, max_accept=1,
          base_rank=GAP)
        Init 4 open card per with base king. Build down in rank alone. Play
        top. Base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9c96, name='Good Measure', game_type=(GT.BAKERS_DOZEN
                    | GT.OPEN), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        _create_game([rows=10, ncols=5]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.gypsy` for keyword definitions.
        """
        kwdefault(kw, rows=10, ncols=5)
        return super(GoodMeasure, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return 2 ACE on top for _foundation_ deal then KING as base
        of pile for _row_ deal.
        """
        cards = place.MoveToTop(cards, place.AllAce, 2)
        base = place.placeBaseCard(cards[:-2], len(self.s['rows']), cs.KING)
        return base + cards[-2:]

    def _start(self, **kw):
        """
        _start([flip=(True) * 4]) -> None

        Deal ace per 2 foundation. Deal 4 card per row open with base king.
        """
        kw.setdefault('flip', (True,) * 4)
        self.s['talon'].dealBaseCards(ncards=2, saveinfo=False)
        super(GoodMeasure, self)._start(**kw)


class Cruel(Martha):
    """
    Finish 1 deck on foundation with row open.

    :CVariables:
      _layout_ : layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [12 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.Cruel_Talon (max_rounds=cs.INFINITE)
        Redeal per row pile up to 4 card each. Inifinite redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Init ace per. Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SS_RowStack (base_rank=GAP)
        Init 4 open card per. Build down in rank by same suit. Play sequence.
        Base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9ca8, name='Cruel', game_type=(GT.BAKERS_DOZEN |
                    GT.OPEN), decks=1, redeals=cs.INFINITE, version='3.00',
                    skill_level=SL.BALANCED)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = Wrap(TalonStack.Cruel_Talon, max_rounds=cs.INFINITE)
    _row_ = Wrap(stack.SS_RowStack, base_rank=cs.GAP)

    def _start(self, **kw):
        """
        _start([flip=(True) * 3]) -> None

        Deal base ace per foundation. Deal 4 card per row open.
        """
        kw.setdefault('flip', (True,) * 3)
        return super(Cruel, self)._start(**kw)


class RoyalFamily(Cruel):
    """
    Finish 1 deck on foundation with row open.

    :CVariables:
      _layout_ : layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [12 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.Cruel_Talon (max_rounds=2)
        Redeal per row pile up to 4 card each. 1 redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (base_rank=KING, dir=-1)
        Init king per. Build down in rank by same suit. Play top. Base king.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_AC_RowStack
        Init 4 open card per. Build either in rank by alt color. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9d63, name='Royal Family', game_type=(GT.BAKERS_DOZEN
                    | GT.OPEN), decks=1, redeals=1, version='0.80fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1)
    _talon_ = Wrap(TalonStack.Cruel_Talon, max_rounds=2)
    _row_ = stack.UD_AC_RowStack

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return KING on top by rank, suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllKing)


class Indefatigable(Cruel):
    """
    Finish 1 deck on foundation with row open.

    :CVariables:
      _layout_ : layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [12 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.Cruel_Talon (max_rounds=3)
        Redeal per row pile up to 4 card each. 2 redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation (max_move=0)
        Init ace per. Build up in rank by same suit. Play none. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack
        Init 4 open card per. Build either in rank by same suit. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9e43, name='Indefatigable',
                    game_type=(GT.BAKERS_DOZEN | GT.OPEN), decks=1, redeals=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = Wrap(stack.SS_Foundation, max_move=0)
    _talon_ = Wrap(TalonStack.Cruel_Talon, max_rounds=3)
    _row_ = stack.UD_SS_RowStack


class Perseverance(Cruel):
    """
    Finish 1 deck on foundation with row open.

    :CVariables:
      _layout_ : layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [12 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.Cruel_Talon (max_rounds=3)
        Redeal per row pile up to 4 card each. 2 redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Init ace per. Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack (base_rank=GAP)
        Init 4 open card per. Build either in rank by same suit. Play top.
        base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint

    :seealso:
      http://en.wikipedia.org/wiki/Perseverance_%28solitaire%29
    """
    __info__ = dict(id=0x9dd4, name='Perseverance', game_type=(GT.BAKERS_DOZEN
                    | GT.OPEN), decks=1, redeals=2, skill_level=SL.BALANCED,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(TalonStack.Cruel_Talon, max_rounds=3)
    _row_ = Wrap(stack.SS_RowStack, base_rank=cs.GAP)


class RippleFan(CastlesInSpain):
    """
    Finish 1 deck on foundation with only row top open.  Row build same suit
    and base none.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [13 over 2 row NW, vert offset]
        - talon [1 SE, no offset]
        - foundation [4 vert E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.Cruel_Talon (max_rounds=INFINITE)
        Redeal per row pile up to 4 card each. Infinite redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.SS_Foundation
        Build up in rank by same suit. Play top. Base ace.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.SS_RowStack (base_rank=GAP)
        Init 4 card per. Build down in rank by same suit. Play sequence. Base
        none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x9db1, name='Ripple Fan', game_type=GT.BAKERS_DOZEN,
                    decks=1, redeals=cs.INFINITE, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(TalonStack.Cruel_Talon, max_rounds=cs.INFINITE)
    _row_ = Wrap(stack.SS_RowStack, base_rank=cs.GAP)

    def _start(self, **kw):
        """
        _start([flip=(True) * 3]) -> None

        Deal 4 card per row open.
        """
        kw.setdefault('flip', (True,) * 3)
        return super(RippleFan, self)._start(**kw)


PROVIDES = [CastlesInSpain, Martha, BakersDozen, SpanishPatience, GoodMeasure,
            Cruel, RoyalFamily, PortugueseSolitaire, Perseverance, RippleFan,
            Indefatigable, SpanishPatienceII]